import os, sys, unittest, subprocess, bs4, shutil, re, json
import common

class HexoRender(common.Render):

    HEXO_TAGS = "tags"
    HEXO_CATEGORIES = "categories"

    FRONT_MASTER = {"title": "title",
            "date": "date",
            "updated": "updated",
            "comments": "comments",
            "permalink": "permalink"
            }

    def __init__(self, base_dir, out_dir):
        self.BASE_DIR = base_dir
        self.OUT_DIR = out_dir
        self.PATTERN_RES_HREF= re.compile('^[^#(\\w+://)].*')

    def _quote(self, s):
        return json.dumps(s, ensure_ascii = False)

    def before_export(self, fpath):
        # make assert dir: if fpath = /path/to/file.org, then mkdir /path/to/.file
        fdir = os.path.dirname(fpath)
        fname = os.path.basename(fpath)
        fname, ext = os.path.splitext(fname)
        res_dir = os.path.join(fdir, '.' + fname)
        if not os.path.isdir(res_dir):
            os.mkdir(res_dir)
        return True

    def export(self, fpath):
        ''' 
        call emacs, org-html-export-to-html
        '''
        print("### Exporting %s" % fpath)
        path = os.path.dirname(fpath)
        fname = os.path.basename(fpath)
        init_file = os.path.join(sys.path[0], 'config.emacs')
        # print("Using emacs init file: %s" % init_file)
        # cmd = ["emacs", fname, "--batch", "-l", init_file, "-f", "org-html-export-to-html", "-kill"]
        cmd = ['emacsclient', '-e', 
                "(progn (find-file \"%s\") (org-html-export-to-html) (kill-buffer))" % fname]
        with subprocess.Popen(cmd, cwd = path, 
                stdout = subprocess.PIPE, stderr = subprocess.PIPE) as fd:
            while True:
                line = fd.stdout.readline()
                if line is None: break
                line = str(line, 'utf-8')
                if len(line) == 0: break
                print(line, end = None)
            while True:
                line = fd.stderr.readline()
                if line is None: break
                line = str(line, 'utf-8')
                if len(line) == 0: break
                print(line, end = None)
        return True

    def _category(self, org_path):
        org_path = os.path.dirname(org_path)
        html_relpath = os.path.relpath(org_path, self.BASE_DIR)
        if html_relpath == '.':
            return None
        return common.split_path(html_relpath)

    def _tags(self, soup):
        retn = []
        tags = soup.find_all(class_ = "tag")
        for tag in tags:
            tag_name = None
            for child in tag.descendants:
                tag_name = child
            if tag_name is not None:
                if tag_name not in retn:
                    retn.append(tag_name)
        return retn

    def _convert_pre(self, soup):
        pres = soup.find_all('pre')
        for pre in pres:
            s_pre = str(pre)
            strings = pre.strings
            if not strings:
                continue
            lst = []
            for t in strings:
                if '{%' in t or '{#' in t or '{{' in t:
                    lst.append(t)
            for t in lst:
                t.replace_with(' '.join([
                    '{% raw %}', 
                    t, 
                    '{% endraw %}'
                ]))

    def _convert_code(self, soup):
        codes = soup.find_all('code')
        for code in codes:
            code.replace_with(' '.join([
                '{% raw %}',
                code.text,
                '{% endraw %}'
            ]))

    def after_export(self, org_path):
        res_path, ext = os.path.splitext(org_path)
        html_path = "%s.html" % res_path
        res_dirname = '.%s' % os.path.basename(res_path)
        if not os.path.isfile(html_path):
            print("Exported html not found: %s" % html_path)
            return False
        # build exported html content
        content = []
        content.append('---')
        fd = open(html_path)
        soup = bs4.BeautifulSoup(fd, 'lxml')
        # hexo front-master
        content.append('title: %s' % self._quote(soup.title.text))
        marks = soup.find_all('meta')
        for mark in marks:
            front_master = self.FRONT_MASTER.get(mark.name)
            if front_master is None: continue
            content.append("%s: %s" % (front_master, mark.content))
        ## fixing: date
        tags = soup.find_all('p', class_ = 'date')
        for tag in tags:
            date = tag.decode_contents()
            if date is None or len(date) == 0: continue
            m = [(m.start(0), m.end(0)) for m in re.finditer('\\d{2,4}\\d{1,2}\\d{1,2}.*(\\d+:\\d+)*', date)]
            if len(m) == 0: continue
            m = m[0]
            date = 'date: %s' % date[m[0]: m[1]]
            print(date)
            content.append(date)
            break
        ## fixing: remove title
        tags = soup.find_all('h1', class_ = 'title')
        for tag in tags:
            tag.extract()
        # hexo category
        category = self._category(org_path)
        if category is not None:
            content.append("%s:" % self.HEXO_CATEGORIES)
            for ele in category:
                content.append("- %s" % ele)
        # hexo tags
        tags = self._tags(soup)
        content.append("%s:" % self.HEXO_TAGS)
        for ele in tags:
            content.append("- %s" % ele)
        content.append("---") # front-master end
        # convert html link
        index = len(res_dirname) + 1
        tag_a = soup.find_all('a', href = self.PATTERN_RES_HREF)
        # tag_a = soup.find_all('a', href = lambda v: v is not None and not v.startswith('#'))
        for ele in tag_a:
            href = ele.get('href')
            print("Replacing %s" % href, end = '')
            href = os.path.normpath(href)
            if href.startswith(res_dirname):
                href = href[index:]
                ele['href'] = href
                print(" with %s" % href)
        tag_img = soup.find_all('img', src = self.PATTERN_RES_HREF)
        for ele in tag_img:
            src = ele.get('src')
            print("Replacing %s" % src, end = '')
            src = os.path.normpath(src)
            if src.startswith(res_dirname):
                src = src[index:]
                ele['src'] = src
                print(" with %s" % src)
        self._convert_pre(soup)
        self._convert_code(soup)
        # append html body only
        html_body = soup.body
        if html_body is None:
            print("Warning: body is None: %s" % org_path)
        else:
            # content.append(html_body.prettify())
            content.append(html_body.decode_contents())
        fd.close()
        # write html
        target_dir = self.OUT_DIR
        if category is not None:
            for ele in category:
                target_dir = os.path.join(target_dir, ele)
        if not os.path.isdir(target_dir):
            os.makedirs(target_dir)
        fd = open(os.path.join(target_dir, os.path.basename(html_path)), 'w')
        content = '\n'.join(content)
        fd.write(content)
        fd.close()
        # remove exported file
        os.remove(html_path)
        # copy res dir
        # res_path = os.path.join(os.path.dirname(org_path), org_dirname)
        # if os.path.isdir(res_path):
        #     target_path = os.path.join(target_dir, org_dirname)
        #     if os.path.isdir(target_path):
        #         shutil.rmtree(target_path)
        #     shutil.copytree(res_path, target_path)
        return True

class _TestClass(unittest.TestCase):

    def __inst(self):
        base_dir = os.path.join(sys.path[0], 'tests')
        out_dir = os.path.join(base_dir, 'out')
        return HexoRender(base_dir, out_dir)

    def test_export(self):
        inst = self.__inst()
        inst.export(os.path.join(sys.path[0], 'tests/test-page.org'))
        pass

if __name__ == "__main__":
    unittest.main()
